In [311]:
import pandas as pd
import numpy as np
import yfinance as yf
import random
from matplotlib import pyplot as plt
import seaborn as sns
import warnings
import secrets
import plotly as plotly
import plotly.express as px
from tqdm import tqdm
import plotly.graph_objects as go
pd.set_option('plotting.backend', 'matplotlib')
plotly.offline.init_notebook_mode()

#warnings.simplefilter(action='ignore', category=FutureWarning

import plotly.io as pio
plotly_template = pio.templates["plotly_dark"]
plt.style.use('dark_background')



#sas

PORTFOLIOS

In [312]:
TICKERS=["SPY","FTSEMIB.MI","ITSC.MI"]
NUMBER_OF_SIMULATIONS=1000
STARTING_CAPITAL = 10000
YEARS_OF_SIMULATION=50
In [ ]:
%%sql
In [314]:
YEARS_OF_SIMULATION=YEARS_OF_SIMULATION+1
Market_Days=253

df_Tickers={x: yf.download(x)["Adj Close"].pct_change(1).dropna() for x in TICKERS}
[*********************100%%**********************]  1 of 1 completed
[*********************100%%**********************]  1 of 1 completed
[*********************100%%**********************]  1 of 1 completed
In [315]:
df_simulations={x: np.zeros((YEARS_OF_SIMULATION,NUMBER_OF_SIMULATIONS)) for x in TICKERS}
for TICKER in TICKERS:
    df_local=df_simulations[TICKER]
    df_local[0, :] = STARTING_CAPITAL
    df_simulations[TICKER]=df_local
    for x in tqdm(range(0,NUMBER_OF_SIMULATIONS)):
        _=np.array((df_Tickers[TICKER].iloc[:]))
        for i in range(1,YEARS_OF_SIMULATION):
            sample=np.random.choice(_,Market_Days)+1
            annual_change=np.prod(sample)
            df_simulations[TICKER][i,x]=annual_change*df_simulations[TICKER][i-1,x]
df_simulations={x: pd.DataFrame(df_simulations[x]) for x in TICKERS}
df_simulations_1=  df_simulations
100%|██████████| 1000/1000 [00:01<00:00, 817.59it/s]
100%|██████████| 1000/1000 [00:01<00:00, 794.19it/s]
100%|██████████| 1000/1000 [00:01<00:00, 767.94it/s]
In [315]:
 
In [316]:
quantile={x: list(df_simulations[x].iloc[YEARS_OF_SIMULATION-1].quantile([0.1,0.9])) for x in TICKERS}
print(quantile)
df_simulations_purged={x: 40 for x in TICKERS}
print(df_simulations_purged)
for TICKER in TICKERS:
    df_local=df_simulations[TICKER]
    df_simulations_purged[TICKER]=df_local[df_local.columns[ df_local.max() < quantile[TICKER][1]]]
    df_simulations_purged[TICKER]=df_simulations_purged[TICKER][df_simulations_purged[TICKER].columns[ df_simulations_purged[TICKER].max() > quantile[TICKER][0]]]
{'SPY': [216510.04428352288, 6699057.775320291], 'FTSEMIB.MI': [1906.3978623624946, 141157.62034518042], 'ITSC.MI': [2987.1184223866517, 119661.80524388277]}
{'SPY': 40, 'FTSEMIB.MI': 40, 'ITSC.MI': 40}
In [317]:
for TICKER in TICKERS:
    print(df_simulations_purged[TICKER].shape)
(51, 808)
(51, 848)
(51, 859)
In [318]:
#for TICKER in TICKERS:
#   df_simulations_purged[TICKER].plot(figsize=(16,8), title=f"Simulation of {TICKER} {NUMBER_OF_SIMULATIONS} portfolios", legend=False)
In [319]:
for TICKER in TICKERS:
    df_simulations_purged[TICKER].iloc[YEARS_OF_SIMULATION-1].plot.density(figsize=(16,8),fontsize=14, xlim=(-10000,900000), label=TICKER , legend= True)
No description has been provided for this image
In [320]:
df_statistics= {x: 0 for x in TICKERS}
for TICKER in TICKERS:
    top_25 =[]
    low_25 =[]
    median=[]
    for i in range(0,YEARS_OF_SIMULATION):
        top_25.append(df_simulations[TICKER].iloc[i].quantile(0.75))
        low_25.append(df_simulations[TICKER].iloc[i].quantile(0.25))
        median.append(df_simulations[TICKER].iloc[i].median())
    columns=["top 25%","median","bottom 25%"]
    df_statistics[TICKER]=pd.DataFrame(list(zip(top_25,median,low_25)),columns=columns)
In [321]:
#ax= df_statistics.plot(legend=None,logy=False,fontsize=20,figsize=(25,15),linewidth=4,color="black",title=f"Simulation of {NUMBER_OF_SIMULATIONS} portfolios")
pd.set_option('plotting.backend', 'plotly')
for TICKER in TICKERS:
    fig = df_statistics[TICKER].plot(width=1600, height=800,title=f"Simulation of {NUMBER_OF_SIMULATIONS} {TICKER} portfolios",labels= {"index": "Years ", "value":"Total Capital"} ,template='plotly_dark')
    fig.add_trace(go.Scatter(x=list(range(0,YEARS_OF_SIMULATION)),y=np.array(df_statistics[TICKER]["top 25%"]),fill='tonexty',mode='lines', line_color='blue' , fillcolor ="red", showlegend=False, hoverinfo="skip"))    
    fig.add_trace(go.Scatter(x=list(range(0,YEARS_OF_SIMULATION)),y=np.array(df_statistics[TICKER]["median"]),fill='tonexty',mode='lines', line_color='orange', fillcolor="green", showlegend=False, hoverinfo="skip"))
    fig.show()
In [322]:
df_simulations_at_profit={x:0 for x in TICKERS}
for TICKER in TICKERS:
    ls_simulations_at_profit=[]
    for i in range(YEARS_OF_SIMULATION):
        c=len([1 for i in list(df_simulations[TICKER].iloc[i])if i > STARTING_CAPITAL])
        ls_simulations_at_profit.append(c/NUMBER_OF_SIMULATIONS*100)
    pd.set_option('plotting.backend', 'plotly')
    df_simulations_at_profit[TICKER] = pd.DataFrame(ls_simulations_at_profit,columns=["Profit %"])
    fig=df_simulations_at_profit[TICKER].plot(title=f"Minimum investment horizon of {TICKER}", labels= {"index": "Period length in years", "value":"Chance to be in profit"},template='plotly_dark')
    fig.show()
    
    
    
In [323]:
for TICKER in TICKERS:
    print(f"The median return of {TICKER} is {round((((np.median(np.array(df_simulations[TICKER].iloc[YEARS_OF_SIMULATION-1,:]))/STARTING_CAPITAL)**(1/YEARS_OF_SIMULATION))-1)*100,2)}%")
The median return of SPY is 9.74%
The median return of FTSEMIB.MI is 0.91%
The median return of ITSC.MI is 1.05%
In [324]:
def drawdown(a):
    a= a.pct_change().dropna()
    acc_max = np.maximum.accumulate(a)
    return (a - acc_max).min()
median_Drawdown={x : [] for x in TICKERS}
for TICKER in TICKERS:
    for s in range(NUMBER_OF_SIMULATIONS-1):
        series=df_simulations[TICKER].iloc[:,s]
        d=drawdown(series)
        median_Drawdown[TICKER].append(d)
    print(f"The median drawdown of {TICKER} is {round(np.median(median_Drawdown[TICKER])*100,2)}%")
The median drawdown of SPY is -90.11%
The median drawdown of FTSEMIB.MI is -107.97%
The median drawdown of ITSC.MI is -101.48%
In [324]:
 
In [325]:
#TICKERS=["AAPL", "DB"]
df_joined=yf.download(TICKERS)["Adj Close"].pct_change(1).dropna()

df_joined.corr()
[*********************100%%**********************]  3 of 3 completed
C:\Users\dadoi\AppData\Local\Temp\ipykernel_20460\867487608.py:2: FutureWarning:

The default fill_method='pad' in DataFrame.pct_change is deprecated and will be removed in a future version. Call ffill before calling pct_change to retain current behavior and silence this warning.

Out[325]:
FTSEMIB.MI ITSC.MI SPY
FTSEMIB.MI 1.000000 0.454097 0.551700
ITSC.MI 0.454097 1.000000 0.244392
SPY 0.551700 0.244392 1.000000
In [326]:
rows=[]

for TICKER in  TICKERS:
    for TICKER_1 in TICKERS:
        Mak_Score=round(np.median((np.array(df_joined[TICKER_1][(df_joined[TICKER_1]>0) & (df_joined[TICKER]>0) ])+1e-10)/(np.array(df_joined[TICKER][(df_joined[TICKER_1]>0) & (df_joined[TICKER]>0) ])+1e-10)),2) - round(np.median((np.array(df_joined[TICKER_1][(df_joined[TICKER_1]<0) & (df_joined[TICKER]<0) ])+1e-10)/(np.array(df_joined[TICKER][(df_joined[TICKER_1]<0) & (df_joined[TICKER]<0) ])+1e-10)))
        
        rows.append([TICKER,TICKER_1,Mak_Score])

df_Mak=pd.DataFrame(data=rows, columns=["tick1","tick2","Mak coefficent"])
df_Mak
Out[326]:
tick1 tick2 Mak coefficent
0 SPY SPY 0.00
1 SPY FTSEMIB.MI 0.34
2 SPY ITSC.MI -0.13
3 FTSEMIB.MI SPY -0.25
4 FTSEMIB.MI FTSEMIB.MI 0.00
5 FTSEMIB.MI ITSC.MI -0.37
6 ITSC.MI SPY 0.14
7 ITSC.MI FTSEMIB.MI -0.42
8 ITSC.MI ITSC.MI 0.00

/

PORTOFLIO OPTIMISATION

In [327]:
df_simulations_yr_={x: np.zeros((YEARS_OF_SIMULATION,len(TICKERS))) for x in TICKERS}
df_joined=yf.download(TICKERS)["Adj Close"].pct_change(1).dropna()
df_joined=df_joined[TICKERS]
df_joined_3=df_joined
f_matrix= df_joined.to_numpy()
f_matrix
[*********************100%%**********************]  3 of 3 completed
C:\Users\dadoi\AppData\Local\Temp\ipykernel_20460\840832971.py:2: FutureWarning:

The default fill_method='pad' in DataFrame.pct_change is deprecated and will be removed in a future version. Call ffill before calling pct_change to retain current behavior and silence this warning.

Out[327]:
array([[ 0.03218813,  0.04007147,  0.01354742],
       [ 0.00307504,  0.00372188,  0.01045435],
       [ 0.01762429,  0.01071676,  0.01022937],
       ...,
       [ 0.00109316,  0.00867773,  0.        ],
       [ 0.00543889, -0.00593315,  0.        ],
       [-0.001588  ,  0.00733902,  0.48598109]])
In [328]:
#maxe a list of al possible combinations without repetition of all natural numbers from 0 to 10 where the sum of the components is always 10
import numpy as np
def all_possible_combinations_Mak(n,k):
    # Create a 3D grid of all possible combinations of [i, j, k]
    
    grid = np.indices((n + 1 for _ in range(k)))

    # Sum along the last axis to check if i + j + k == n
    sum_grid = grid.sum(axis=0)
 
    # Find indices where the sum equals n
    valid_indices = np.argwhere(sum_grid == n)
    valid_indices=np.divide(valid_indices,n)
    return valid_indices
i=10
q=10000
a=all_possible_combinations_Mak(i,len(TICKERS))
while True:
    if len(a)<q:
        a=all_possible_combinations_Mak(i,len(TICKERS))
        i=round(i*(np.log2(q/len(a))))
    else:
        break
        
In [329]:
i=0
return_matrix=np.zeros((len(f_matrix),len(a)))
for row in tqdm(a):
    j=0
    for pair in f_matrix:
        #return_matrix[j,i]=np.sum([pair[x]*row[x] for x in range(0,len(TICKERS))])
        return_matrix[j,i]=np.dot(pair,row)
        j=j+1
    i=i+1
100%|██████████| 10878/10878 [01:24<00:00, 129.15it/s]
In [330]:
plane_of_returns=[]
i=0
j=0
Rf=yf.download("^TNX")["Adj Close"].dropna().iloc[-1]*0.01
print(Rf)
#return_matrix=np.flip(return_matrix)
for x in return_matrix.T:
    median=np.median(x)*Market_Days
    values=a[i]
    mean=np.mean(x)*Market_Days
    std=np.std(x)*Market_Days
    sharpe_ratio=(mean-Rf)/std
    plane_of_returns.append(np.append(values ,[median ,mean,std,sharpe_ratio]))
    i=i+1
plane_of_returns=np.array(plane_of_returns)
df_plane=pd.DataFrame(plane_of_returns,columns=TICKERS+["Median","Mean","Std","Sharpe_ratio"])
df_plane
[*********************100%%**********************]  1 of 1 completed
0.04147000312805176
Out[330]:
SPY FTSEMIB.MI ITSC.MI Median Mean Std Sharpe_ratio
0 0.000000 0.000000 1.000000 0.000000 0.019055 2.652497 -0.008451
1 0.000000 0.006849 0.993151 0.004012 0.019192 2.645945 -0.008420
2 0.000000 0.013699 0.986301 0.007939 0.019330 2.639587 -0.008388
3 0.000000 0.020548 0.979452 0.011717 0.019468 2.633424 -0.008355
4 0.000000 0.027397 0.972603 0.015623 0.019606 2.627458 -0.008321
... ... ... ... ... ... ... ...
10873 0.986301 0.006849 0.006849 0.139688 0.114147 2.942929 0.024695
10874 0.986301 0.013699 0.000000 0.140439 0.114284 2.952608 0.024661
10875 0.993151 0.000000 0.006849 0.134882 0.114668 2.949116 0.024820
10876 0.993151 0.006849 0.000000 0.140891 0.114806 2.958693 0.024787
10877 1.000000 0.000000 0.000000 0.134472 0.115328 2.964931 0.024910

10878 rows × 7 columns

​Sharpe Ratio=σp​ Rp​−Rf​​

In [331]:
hover=""
i=0
for TICKER in TICKERS: 
    if i %2 == 0:
        hover=hover+'<b>'+TICKER+': %{customdata['+str(i)+']:.4f}</b>'
    else:
        hover=hover+'<br><b>'+TICKER+': %{customdata['+str(i)+']:.4f}</b></br>'
    i=i+1
hover=hover+'<br><b>Standard Deviation: %{x:.3f}</b></br> '+ '<b>Mean: %{y:.3f}</b>'+ '<br><b>Sharpe Ratio %{text:.4f}</b></br>'

fig=go.Figure()
fig.add_trace(go.Scatter(x=df_plane["Std"], y=df_plane["Mean"], mode='markers',marker=dict(size=10,color=df_plane["Sharpe_ratio"],colorscale='Viridis',showscale=True),hovertemplate =hover
 ,customdata=np.stack((df_plane[TICKER] for TICKER in TICKERS), axis=-1),text=df_plane["Sharpe_ratio"]))
fig.update_layout(width=1600, height=800)
fig.update_layout(
    hoverlabel=dict(
        bgcolor="white",
        font_size=16
    ),
)

fig.show()
C:\Users\dadoi\anaconda3\Lib\site-packages\IPython\core\interactiveshell.py:3466: FutureWarning:

arrays to stack must be passed as a "sequence" type such as list or tuple. Support for non-sequence iterables such as generators is deprecated as of NumPy 1.16 and will raise an error in the future.

In [331]:
 
In [331]:
 
In [331]:
 
In [331]:
 
In [ ]:
!jupyter nbconvert --to html Portfolio1.ipynb --HTMLExporter.theme=dark
In [ ]: